%{
#include "../ast/ast.h"
typedef enum { typeKeyword, typeSymbol, typeComment } TokenType;
int yylex();
void yyerror(const char *s);
ProgramAST* root;
%}

/* Generate the parser description file. */
%verbose
/* Enable run-time traces (yydebug). */
%define parse.trace

%define parse.error verbose
%union{
    char* string;
    TokenType token_type;
    
    /* programs and blocks */
    ProgramAST* ast_program;
    ProgramHeadingAST* ast_program_heading;
    ProgramBlockAST* ast_program_body;
    BlockAST* ast_block;
    ProgramParametersAST* ast_program_paramenters;
    IdentifierListAST* ast_identifier_list;

    /*Declarations and Definitions*/
    ConstantDefinitionListAST* ast_constant_definition_list;
    ConstantDefinitionAST* ast_constant_definition;
    TypeDefinitionListAST* ast_type_definition_list;
    TypeDefinitionAST* ast_type_definition;
    VariableDeclarationListAST* ast_variable_declaraition_list;
    VariableDeclarationAST* ast_variable_declaration; 
    ProcedureFunctionDeclarationListAST* ast_procedure_function_declarition;

    /*Subprogram Declarations*/
    SubprogramDeclarationAST* ast_sub_program_declaration;

    /*Procedure Declarations*/
    ProcedureDeclarationAST* ast_procedure_declaration;
    ProcedureHeadingAST* ast_procedure_heading;
    ProcedureBodyAST* ast_procedure_body;
    TypeDenoterAST* ast_type_denoter;

    /*Function Declarations*/
    FunctionDeclarationAST* ast_function_declaration;
    FunctionHeadingAST* ast_function_heading;
    FunctionBodyAST* ast_function_body;
    
    /*Formal Parameters and Arguments*/
    FormalParameterListAST* ast_formal_parameter_list;
    FormalParameterSectionAST* ast_formal_parameter_section;
    ValueParameterSpecificationAST* ast_value_parameter_specification;
    VariableParameterSpecificationAST* ast_variable_parameter_specification;

    /*Constant*/
    ConstantAST* ast_constant; 

    /*Types*/
    TypeIdentifierAST* ast_type_identifier;
    SimpleTypeIdentifierAST* ast_base_type;
    ArrayTypeAST* ast_array_type;
    PeriodListAST* ast_period_list;
    PeriodAST* ast_period;
    RecordTypeAST* ast_record_type;
    FieldListAST* ast_field_decl_list;
    FieldAST* ast_field;

    /*Statements*/
    StatementAST* ast_statement;
    ProcedureStatementAST* ast_procedure_statement;
    IOProcedureStatementAST* ast_io_procedure_statement; 
    ActualParameterListAST* ast_actual_param_list;
    ReadParameterListAST* ast_read_paramenter_list;
    WriteParameterList* ast_write_paramenter_list; 
    ActualParameterAST* ast_actual_param;
    StatementPartAST* ast_statement_part;
    CompoundStatement* ast_compound_statement;
    StatementSequenceAST* ast_statement_sequence;
    
    /*Simple Statement*/
    EmptyStatementAST* ast_empty_statement;
    AssignmentStatementAST* ast_assignment_statement;
    
    /* Conditional Statement*/
    IfStatementAST* ast_if_statement;
    CaseStatementAST* ast_case_statement;
    
    // Repetitive Statement
    RepeatStatementAST* ast_repeat_statement;
    WhileStatementAST* ast_while_statement;
    ForStatementAST* ast_for_statement;
    
    // Related to CaseStatementAST
    CaseListElementListAST* ast_case_element_list;
    CaseListElementAST* ast_case_list_element;
    CaseConstantListAST* ast_case_constant_list;

    /// Expressions and Variables -----------------------------
    ExpressionAST* ast_expression;
    UnsignedConstantAST* ast_unsigned_constant;
    IdentifierAST* ast_identifier;
    BinaryAST* ast_binary;
    IndexedVariableAST* ast_indexed_variable;
    IndexExpressionListAST* ast_index_expression_list;
    IndexExpressionAST* ast_index_expression;
    FieldDesignatorAST* ast_field_designator;
    FunctionDesignatorAST* ast_function_designator;
}


%token<token_type> AND ARRAY BEGINN
%token<token_type> CASE CONST
%token<token_type> KEYWORD_DIV DO ELSE TO DOWNTO
%token<token_type> END FOR FUNCTION 
%token<token_type> IF
%token<token_type> KEYWORD_MOD NOT
%token<token_type> OF OR
%token<token_type> PROCEDURE PROGRAM RECORD
%token<token_type> REPEAT
%token<token_type> THEN TYPE
%token<token_type> UNTIL VAR WHILE
%token<token_type> TYPE_INTEGER TYPE_REAL TYPE_CHAR TYPE_BOOLEAN
%token<token_type> ADD SUB MUL DIV MOD ASSIGN
%token<token_type> GT LT GE LE EQUAL NEQUAL
%token<token_type> DOT COMMA SEMICOLON COLON
%token<token_type> LPAREN RPAREN LBRACK RBRACK
%token<token_type> COMMENT

%token<string> IDENTIFIER LITERAL_INTEGER LITERAL_REAL LITERAL_CHAR LITERAL_BOOLEAN_TRUE LITERAL_BOOLEAN_FALSE LITERAL_MIN_INTEGER LITERAL_MIN_REAL

%type<ast_program> program;
%type<ast_program_heading> program_head;
%type<ast_program_body> program_body;
%type<ast_block> block; 
%type<ast_program_paramenters> program_param_list;
%type<ast_identifier_list> identifier_list;
%type<ast_constant_definition_list> constant_declaration;
%type<ast_constant_definition_list> constant_list;
%type<ast_actual_param_list> actual_param_list;
%type<ast_actual_param> actual_param;
%type<ast_constant_definition> constant_decl; 
%type<ast_type_definition> type_def; 
%type<ast_type_definition_list> type_def_list; 
%type<ast_type_definition_list> type_definition;
%type<ast_variable_declaraition_list> variable_declaration;
%type<ast_variable_declaraition_list> variable_decl_list;
%type<ast_variable_declaration> variable_decl;
%type<ast_procedure_function_declarition> procedure_function_declaration;
//%type<ast_sub_program_declaration>
%type<ast_procedure_declaration> procedure_declaration;
%type<ast_procedure_heading> procedure_head;
%type<ast_procedure_body>procedure_body;
%type<ast_type_denoter> type_denoter;
%type<ast_type_identifier>type_identifier;
%type<ast_function_declaration> function_declaration;
%type<ast_function_heading> function_head;
%type<ast_function_body> function_body;
%type<ast_formal_parameter_list> formal_param_list;
%type<ast_formal_parameter_section> formal_param;
%type<ast_value_parameter_specification> value_param;
%type<ast_variable_parameter_specification> variable_param;
%type<ast_constant> constant;
//%type<ast_type_identifier> 
%type<ast_base_type>base_type;
%type<ast_array_type> array_type;
%type<ast_period_list>periods;
%type<ast_period>period;
%type<ast_record_type>record_type;
%type<ast_field_decl_list> field_decl_list; 
%type<ast_field> field_decl; 
%type<ast_statement> statement; 
%type<ast_procedure_statement> procedure_statement; 
//%type<ast_io_procedure_statement> ; //
//%type<ast_read_paramenter_list> ; //
//%type<ast_write_paramenter_list> ; // ºóÆÚÔÙËµ
%type<ast_statement_part> statement_part; 
%type<ast_compound_statement> compound_statement; 
%type<ast_statement_sequence> statement_list; 
//%type<ast_empty_statement> ; //ºöÂÔ
%type<ast_assignment_statement> assign_statement; 
%type<ast_if_statement> if_statement; 
%type<ast_case_statement> case_statement; 
%type<ast_repeat_statement> repeat_statement; 
%type<ast_while_statement> while_statement; 
%type<ast_for_statement> for_statement; 
%type<ast_case_element_list> branch_list; 
%type<ast_case_element_list> case_body;
%type<ast_case_list_element> branch; 
%type<ast_case_constant_list> case_constant_list; //
%type<ast_expression> expression; 
//%type<ast_unsigned_constant> ; 
//%type<ast_identifier> ; //
%type<ast_expression> relational_expression; //relation term factor ¶þÔªÔËËã
%type<ast_expression> term;
%type<ast_expression> factor;
//%type<ast_indexed_variable> ; 
//%type<ast_index_expression_list> ; //array
//%type<ast_index_expression> ; //array
//%type<ast_field_designator> ; //record
//%type<ast_function_designator> ; //


%%
program:
    program_head SEMICOLON  program_body{
        root = new ProgramAST($1,$3);
    };

program_head:
    PROGRAM IDENTIFIER{
        $$ = new ProgramHeadingAST($2,nullptr);
    }
    | PROGRAM IDENTIFIER LPAREN program_param_list RPAREN {
        $$ = new ProgramHeadingAST($2,$4);
    };

program_body:
    block DOT{
        $$ = new ProgramBlockAST($1);
    };

block:
   constant_declaration  type_definition variable_declaration procedure_function_declaration statement_part{
       $$ = new BlockAST($1,$2,$3,$4,$5);
   };

program_param_list:
    identifier_list{
        $$ = new ProgramParametersAST($1);
    };

identifier_list:
    identifier_list COMMA IDENTIFIER{
        ($1)->push_back($3);
        $$ = $1;
    }
    | IDENTIFIER{
        $$ = new IdentifierListAST();
        ($$)->push_back($1);
    };


constant_declaration:
    CONST constant_list{
        $$ = $2;
    }
    | /* empty */ {
        $$ = nullptr;
    };

constant_list:
    constant_list constant_decl{
        ($1)->push_back($2);
        $$ = $1;
    }
    | constant_decl{
        $$ = new ConstantDefinitionListAST();
        ($$)->push_back($1);
    };


constant_decl:
    IDENTIFIER EQUAL constant SEMICOLON{
        $$ = new ConstantDefinitionAST($1,$3);
    };

constant:
    IDENTIFIER{
        $$ = new ConstantAST(ConstantAST::Type::IDENTIFIER,$1);
    }
    | LITERAL_INTEGER{
        $$ = new ConstantAST(ConstantAST::Type::INTEGER,$1);
    }
    | LITERAL_REAL{
        $$ = new ConstantAST(ConstantAST::Type::REAL,$1);
    }
    | LITERAL_CHAR{
        $$ = new ConstantAST(ConstantAST::Type::CHARACTER,$1);
    }
    | LITERAL_BOOLEAN_TRUE{
        $$ = new ConstantAST(ConstantAST::Type::BOOLEAN,$1);
    }
    | LITERAL_BOOLEAN_FALSE{
        $$ = new ConstantAST(ConstantAST::Type::BOOLEAN,$1);
    };

type_definition:
    TYPE type_def_list{
        $$ = $2;
    }
    | /* empty */ {
        $$ = nullptr;
    };

type_def_list:
    type_def_list type_def{
        ($1)->push_back($2);
        $$ = $1;
    }
    | type_def{
        $$ = new TypeDefinitionListAST();
        ($$)->push_back($1);
    };

type_def:
    IDENTIFIER EQUAL type_denoter SEMICOLON{
        $$ = new TypeDefinitionAST($1,$3);
    };

type_denoter:
    type_identifier{
        $$ = $1;
    }
    | array_type{
        $$ = $1;
    }
    | record_type{
        $$ = $1;
    };

type_identifier:
    base_type{
        $$ = $1;
    }
    | IDENTIFIER{
        $$ = new TypeIdentifierAST($1);
    };

base_type:
    TYPE_INTEGER{
        $$ = new SimpleTypeIdentifierAST(SimpleTypeIdentifierAST::StandardType::INTEGER);
    }
    | TYPE_REAL{
        $$ = new SimpleTypeIdentifierAST(SimpleTypeIdentifierAST::StandardType::REAL);
    }
    | TYPE_CHAR{
        $$ = new SimpleTypeIdentifierAST(SimpleTypeIdentifierAST::StandardType::CHARACTER);
    }
    | TYPE_BOOLEAN{
        $$ = new SimpleTypeIdentifierAST(SimpleTypeIdentifierAST::StandardType::BOOLEAN);
    };

array_type:
    ARRAY LBRACK periods RBRACK OF type_denoter{
        $$ = new ArrayTypeAST($3,$6);
    };

periods:
     periods DOT period{
        ($1)->push_back($3);
        $$ = $1;
     }
    | period{
        $$ = new PeriodListAST();
        ($$)->push_back($1);
    };

period : 
    constant DOT DOT constant{
        $$ = new PeriodAST($1,$4);
    };

record_type:
    RECORD field_decl_list END{
        $$ = new RecordTypeAST($2);
    };

field_decl_list:
    field_decl_list field_decl{
        ($1)->push_back($2);
        $$ = $1;
    }
    | field_decl{
        $$ = new FieldListAST();
        ($$)->push_back($1);
    };

field_decl:
    identifier_list COLON type_denoter SEMICOLON{
        $$ = new FieldAST($1,$3);
    };

variable_declaration:
    VAR variable_decl_list{
        $$ = $2;
    }
    | /* empty */ {
        $$ = nullptr;
    };

variable_decl_list:
    variable_decl_list variable_decl{
        ($1)->push_back($2);
        $$ = $1;
    }
    | variable_decl{
        $$ = new VariableDeclarationListAST();
        ($$)->push_back($1);
    };

variable_decl:
    identifier_list COLON type_denoter SEMICOLON{
        $$ = new VariableDeclarationAST($1,$3);
    };

procedure_function_declaration:
    procedure_function_declaration procedure_declaration{
        ($1)->push_back($2);
        $$ = $1;
    }
    | procedure_function_declaration function_declaration{
        ($1)->push_back($2);
        $$ = $1;
    }
    | procedure_declaration{
        $$ = new ProcedureFunctionDeclarationListAST();
        ($$)->push_back($1);
    }
    | function_declaration{
        $$ = new ProcedureFunctionDeclarationListAST();
        ($$)->push_back($1);
    }
    | /* empty */ {
        $$ = nullptr;
    } ;

procedure_declaration:
    procedure_head SEMICOLON procedure_body{
        $$ = new ProcedureDeclarationAST($1,$3);
    };

procedure_head:
    PROCEDURE IDENTIFIER LPAREN formal_param_list RPAREN{
        $$ = new ProcedureHeadingAST($2,$4);
    }
    | PROCEDURE IDENTIFIER LPAREN RPAREN{
        $$ = new ProcedureHeadingAST($2,new FormalParameterListAST());
    };;

procedure_body:
    block SEMICOLON{
        $$ = new ProcedureBodyAST($1);
    };

function_declaration:
    function_head SEMICOLON function_body{
        $$ = new FunctionDeclarationAST($1,$3);
    };

function_head:
    FUNCTION IDENTIFIER LPAREN formal_param_list RPAREN COLON base_type{
        $$ = new FunctionHeadingAST($2,$4,$7);
    }
    | FUNCTION IDENTIFIER LPAREN RPAREN COLON base_type{
        $$ = new FunctionHeadingAST($2,new FormalParameterListAST(),$6);
    };

function_body:
    block SEMICOLON{
        $$ = new  FunctionBodyAST($1);
    };

formal_param_list:
    formal_param_list SEMICOLON formal_param{
        ($1)->push_back($3);
        $$ = $1;
    }
    | formal_param{
        $$ = new FormalParameterListAST();
        ($$)->push_back($1);
    };

formal_param:
    value_param{
        $$ = $1;
    }
    | variable_param{
        $$ = $1;
    };

value_param:
    identifier_list COLON type_identifier{
        $$ = new  ValueParameterSpecificationAST($1,$3);
    };

variable_param:
    VAR identifier_list COLON type_identifier{
        $$ = new  VariableParameterSpecificationAST($2,$4);
    };

actual_param_list:
    actual_param_list COMMA actual_param{
        ($1)->push_back($3);
    }
    | actual_param{
        $$ = new ActualParameterListAST();
        ($$)->push_back($1);
    };

actual_param:
    relational_expression{
        $$ = new ActualParameterAST($1);
    };

statement_part:
    compound_statement{
        $$ = new StatementPartAST($1);
    }
    | /* empty */ {
        $$ = nullptr;
    };

compound_statement:
    BEGINN statement_list END{
        $$ = new CompoundStatement($2);
    };

statement_list:
    statement_list statement SEMICOLON{
        ($1)->push_back($2);
        $$ = $1;
    }
    | statement SEMICOLON{
        $$ = new StatementSequenceAST();
        ($$)->push_back($1);
    };

statement:
    assign_statement{
        $$ = $1;
    }
    | procedure_statement{
        $$ = $1;
    }
    | if_statement{
         $$ = $1;
    }
    | repeat_statement{
        $$ = $1;
    }
    | while_statement{
        $$ = $1;
    }
    | compound_statement{
        $$ = $1;
    }
    | for_statement{
        $$ = $1;
    }
    | case_statement{
        $$ = $1;
    }
    | /*empty*/{
        $$ = new EmptyStatementAST();
    };

for_statement:
    FOR IDENTIFIER ASSIGN expression TO expression DO statement{
        IdentifierAST* id = new IdentifierAST($2);
        $$ = new ForStatementAST(id,$4,ForStatementAST::ForDirection::OP_TO,$6,$8);
    }
    |FOR IDENTIFIER ASSIGN expression DOWNTO expression DO statement{
        IdentifierAST* id = new IdentifierAST($2);
        $$ = new ForStatementAST(id,$4,ForStatementAST::ForDirection::OP_DOWNTO,$6,$8);
    };

case_statement:
    CASE  relational_expression  OF  case_body  END{
        $$ = new CaseStatementAST($2,$4);
    };

case_body:
    branch_list{
        $$ = $1;
    }
    | /*empty*/{
        $$ = nullptr;
    };

branch_list :
    branch_list SEMICOLON branch{
        ($1)->push_back($3);
        $$ = $1;
    }
    | branch{
        $$ =new CaseListElementListAST();
        ($$)->push_back($1);
    };

branch:
    case_constant_list COLON statement{
        $$ = new CaseListElementAST($1,$3);
    };

case_constant_list:
    case_constant_list COMMA constant{
        ($1)->push_back($3);
        $$ = $1;
    };
    | constant{
        $$ = new CaseConstantListAST();
        ($$)->push_back($1);
    };

assign_statement:
    IDENTIFIER ASSIGN relational_expression{
        /* id = a<b */
        IdentifierAST * left = new IdentifierAST($1);
        $$ = new AssignmentStatementAST(left,$3);
    }
    | IDENTIFIER LBRACK relational_expression RBRACK ASSIGN relational_expression{
       /* id [ a<b ] = a<b */
        IndexExpressionAST* content = new IndexExpressionAST($3);
        IndexExpressionListAST* index = new IndexExpressionListAST();
        index->push_back(content);
        IndexedVariableAST * left = new IndexedVariableAST($1,index);
        $$ = new AssignmentStatementAST(left,$6);
    }
    | IDENTIFIER DOT IDENTIFIER ASSIGN relational_expression{
        /* id . id = a<b */
        FieldDesignatorAST * left = new FieldDesignatorAST($1,$3);
        $$ = new AssignmentStatementAST(left,$5);
    };


procedure_statement:
    IDENTIFIER LPAREN actual_param_list RPAREN{
         $$ = new ProcedureStatementAST($1,$3);
    }
    | IDENTIFIER LPAREN RPAREN{
         $$ = new ProcedureStatementAST($1,new ActualParameterListAST());
    };

if_statement:
    IF relational_expression THEN statement{
        $$ = new IfStatementAST($2,$4,nullptr);
    }
    | IF relational_expression THEN statement ELSE statement{
        $$ = new IfStatementAST($2,$4,$6);
    };

repeat_statement:
    REPEAT statement_list UNTIL relational_expression{
        $$ = new RepeatStatementAST($2,$4);
    };

while_statement:
    WHILE relational_expression DO statement{
        $$ = new WhileStatementAST($2,$4);
    };

relational_expression:
    expression GT expression{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_GT);
    }
    | expression LT expression{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_LT);
    }
    | expression GE expression{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_GE);
    }
    | expression LE expression{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_LE);
    }
    | expression EQUAL expression{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_EQ);
    }
    | expression NEQUAL expression{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_NE);
    }
	| expression{
        $$ = $1;
    };

expression:
    expression ADD term{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_ADD);
    }
    | expression SUB term{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_SUB);
    }
    | expression OR term{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_OR);
    }
    | term{
        $$ = $1;
    };

term:
    term MUL factor{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_MUL);
    }
    | term KEYWORD_DIV factor{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_DIV);
    }
    | term KEYWORD_MOD factor{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_MOD);
    }
    | term AND factor{
        $$ = new BinaryAST($1,$3,BinaryAST::OpType::OP_AND);
    }
    | factor{
        $$ = $1;
    };

factor:
    constant{
        $$ = new UnsignedConstantAST($1); /*  UnsignedConstantAST : public ExpressionAST  */
    }
    | LPAREN relational_expression RPAREN{
        $$ = $2;
    }
    | SUB factor{
        /* 单操作数情况 */
        $$ = new BinaryAST(nullptr,$2,BinaryAST::OpType::OP_SUB);
    }
    | NOT factor{
        $$ = new BinaryAST(nullptr,$2,BinaryAST::OpType::OP_NOT);
    }
    | IDENTIFIER LPAREN actual_param_list RPAREN{
        $$ = new FunctionDesignatorAST($1,$3);
    }
    | IDENTIFIER LPAREN RPAREN{
        $$ = new FunctionDesignatorAST($1,new ActualParameterListAST());
    }
    | IDENTIFIER LBRACK relational_expression RBRACK{
        IndexExpressionAST* content = new IndexExpressionAST($3);
        IndexExpressionListAST* indice = new IndexExpressionListAST();
        indice->push_back(content);
        $$ = new IndexedVariableAST($1,indice);
    }
    | IDENTIFIER DOT IDENTIFIER{
        $$ = new FieldDesignatorAST($1,$3);
    };
%%

void yyerror (const char *s)
{
    fprintf (stderr, "%s\n", s);
}
